/*Copyright Chris <MA_D> Hilton see ../LICENSE for GPL details  */

#include "status.h"
#include "second.h"
#include "threads.h"
#include "error.h"
#include "settings.h"
#include <gtk/gtk.h>

#define INF -1


GtkWidget *_progress;
GtkWidget *_status;

struct _status_msg {
	 char *message;
	 char *full;
	 int color;
	 int time;
	 int id;
	 struct _status_msg *next;
};

struct _status_msg *_status_msg_list = NULL;

struct _status_msg *_status_msg_old_list = NULL;

GtkRcStyle *_status_style = NULL;

int _status_lock = 0;

int _status_ready = 0;

GtkWidget *_status_btn;




/*Callbacks!*/
void        status_btn_pushed      (GtkButton *widget,
                                            gpointer   user_data) 
{
	 elog_status_show_message_window();
}
/*****************************/



void        _show_message_window_response      (GtkDialog *dialog,
								gint       arg1,
								gpointer   user_data)
{
	 gtk_widget_destroy((GtkWidget *)user_data);
}


void _status_show_list_in_window(struct _status_msg *p, GtkWidget *vbox)
{
	 while (p) {
		  GtkWidget *toColor = NULL;
		  if (p->full != p->message) {
			   GtkWidget *expand = gtk_expander_new(p->message);
			   GtkTextBuffer *buffer = gtk_text_buffer_new(NULL);
			   gtk_text_buffer_set_text(buffer, p->full, -1);
			   GtkWidget *txt = gtk_text_view_new_with_buffer(buffer);
			   gtk_text_view_set_editable(GTK_TEXT_VIEW(txt), FALSE);
			   gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(txt), GTK_WRAP_WORD);

			   GtkWidget *inScroll = gtk_scrolled_window_new(NULL, NULL);
			   gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(inScroll),
											  GTK_POLICY_NEVER,
											  GTK_POLICY_AUTOMATIC);
			   gtk_container_add(GTK_CONTAINER(inScroll), txt);
			   gtk_container_add(GTK_CONTAINER(expand), inScroll);
			   gtk_box_pack_start(GTK_BOX(vbox), expand, FALSE, FALSE, 0);
			   toColor = expand;
		  } else {
			   GtkWidget *label = gtk_radio_button_new_with_label(NULL,
																  p->message);
			   gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
			   toColor = label;
		  }
		  if (p->color) {
			   GdkColor *color = malloc(sizeof *color);
			   color->red = 50000;
			   color->blue = 7000;
			   color->green = 45000;
			   gtk_widget_modify_bg(toColor, GTK_STATE_NORMAL, color);
			   gtk_widget_modify_base(toColor, GTK_STATE_NORMAL, color);
			   gtk_widget_modify_fg(toColor, GTK_STATE_NORMAL, color);
			   free(color);
		  }
		  
		  
		  GtkWidget *sep = gtk_hseparator_new();
		  gtk_box_pack_start(GTK_BOX(vbox), sep, FALSE, FALSE, 0);

		  p=p->next;
	 }
}

void elog_status_show_message_window()
{
	 elog_glob_lock();
	 GtkWidget *window = 
		  gtk_dialog_new_with_buttons("eJourn::Status Messages",
									  GTK_WINDOW(_mainWindow),
									  GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
									  GTK_STOCK_CLOSE,
									  GTK_RESPONSE_CLOSE,
									  NULL);
	 gtk_window_set_default_size(GTK_WINDOW(window), 300, 300);

	 GtkWidget *ovbox = GTK_DIALOG(window)->vbox;


	 gtk_window_set_title(GTK_WINDOW(window), "eJourn::Status Messages");
	 
	 GtkWidget *scroll = gtk_scrolled_window_new(NULL, NULL);
	 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll),
									GTK_POLICY_NEVER,
									GTK_POLICY_AUTOMATIC);

	 GtkWidget *vbox = gtk_vbox_new(FALSE, 0);
	 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), vbox);
	 
	 //Put in the new messages
	 _status_show_list_in_window(_status_msg_list, vbox);

	 GtkWidget *label = gtk_label_new("Old Messages:");
	 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
	 

	 GtkWidget *sep = gtk_hseparator_new();
	 gtk_box_pack_start(GTK_BOX(vbox), sep, FALSE, FALSE, 0);

	 //Put in the old messages...
	 _status_show_list_in_window(_status_msg_old_list, vbox);


	 gtk_box_pack_start(GTK_BOX(ovbox), scroll, TRUE, TRUE, 0);

	 g_signal_connect((gpointer) window, "response",
					 G_CALLBACK(_show_message_window_response), window);

	 gtk_widget_show_all(window);
	 elog_glob_unlock();
}




void *elog_status_initialize() 
{
	GtkWidget *statusbar1;
	GtkWidget *status_box;
	GtkWidget *event = gtk_event_box_new();
	_status = statusbar1 = gtk_statusbar_new();
	//gtk_wiget_show (statusbar1);


	_progress = gtk_progress_bar_new();
	//gtk_wiget_show(_progress);
	gtk_box_pack_end(GTK_BOX(statusbar1), _progress, FALSE, FALSE, 0);
	_status_btn = gtk_button_new_with_label("All Messages");
	gtk_button_set_relief(GTK_BUTTON(_status_btn), GTK_RELIEF_NONE);


	status_box = gtk_hbox_new(FALSE, 0);


	gtk_box_pack_start(GTK_BOX(status_box), _status_btn, FALSE, FALSE, 2);
	gtk_box_pack_start(GTK_BOX(status_box), statusbar1, TRUE, TRUE, 2);

	g_signal_connect((gpointer) _status_btn, "clicked",
					 G_CALLBACK(status_btn_pushed), NULL);
	g_signal_connect((gpointer) _status_btn, "activate",
					 G_CALLBACK(status_btn_pushed), NULL);

	_status_ready = 1;
	gtk_container_add(GTK_CONTAINER(event), status_box);
	return event;
}
int elog_status_ready()
{
	 return _status_ready;
}

void elog_status_hide()
{
	 if (elog_set_get_int("STATUSBAR_HIDE")) {
		  gtk_widget_hide(_status);
		  gtk_widget_hide(_status_btn);
	 }
}

void elog_status_show()
{
	 gtk_widget_show(_status);
	 gtk_widget_show(_status_btn);
}

void elog_status_normal()
{
	 elog_glob_lock();
	 //we're going to see if it makes sense to go normal
	 //and if it does, we will!
	 int go = 1;
	 struct _status_msg *p = _status_msg_list;
	 while (p) {
		  if (p->color) {
			   go = 0;
			   break;
		  }
		  p = p->next;
	 }

	 if (_status_style)
		  gtk_widget_modify_style(_mainWindow, _status_style);

	 elog_glob_unlock();
} //internal function...


void elog_status_progress_pulse()
{
	 elog_glob_lock();
	 gtk_progress_bar_pulse(GTK_PROGRESS_BAR(_progress));
	 elog_glob_unlock();
}

void elog_status_progress_set(float to)
{
	 elog_glob_lock();
	 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(_progress),
								   (double) to);
	 elog_glob_unlock();
}


void elog_status_color(int status, int color)
{
	 struct _status_msg *p = _status_msg_list;
	 for (;p; p=p->next) 
		  if (p->id == status)
			   p->color = color;
}

unsigned int elog_status_push_status(char *status)
{
	 elog_glob_lock();
	 int stat = 0;
	 struct _status_msg *p = _status_msg_list;
	 while (p) {
		  if (strcmp(status, p->full) == 0) {
			   stat = 1;
			   break;
		  }
		  p = p->next;
	 }
	 if (!stat) {
		  int c=0; //we don't want to search too much
		  //at some point memory is cheaper than cpu cycles
		  p = _status_msg_old_list;
		  while (p && c < 25) {
			   if (strcmp(status, p->full) == 0) {
					stat = 1;
					break;
			   }
			   p = p->next;
			   ++c;
		  }
	 }

	 struct _status_msg *msg = malloc(sizeof *msg);
	 if (stat) { //saving some memory, recycling strings
		  msg->full = p->full;
		  msg->message = p->message;
	 } else {
		  elog_sp_cat(&(msg->full), status, NULL);

		  char *loc;
		  if ((loc = strstr(status, "\n")) != NULL) {
			   msg->message = malloc(loc - status + 1);
			   memset(msg->message, '\0', (loc - status + 1)  * (sizeof *(msg->message)));
			   strncat(msg->message, status, loc - status);
		  } else if (strlen(status) > 70) {
			   msg->message = malloc(75);
			   memset(msg->message, '\0', 75 * (sizeof *(msg->message)));
			   strncat(msg->message, status, 70);
			   msg->message[70] = '.';
			   msg->message[71] = '.';
			   msg->message[72] = '.';
		  }else {
			   msg->message = msg->full;
		  }
	 }


	 elog_status_show();
	 elog_status_progress_set(0.0);

	 guint context =
		  gtk_statusbar_get_context_id(GTK_STATUSBAR(_status), msg->message);
	 gtk_statusbar_push(GTK_STATUSBAR(_status), context, msg->message);

	 msg->id = context;
	 msg->time = INF;
	 msg->color = 0;

	 if (!_status_msg_list) {
		  _status_msg_list = msg;
		  msg->next = NULL;
	 } else {
		  msg->next = _status_msg_list;
		  _status_msg_list = msg;
	 }

	 elog_glob_unlock();
	 return (unsigned int) context;
}

void elog_status_pop_status(unsigned int id)
{
	 elog_glob_lock();
	 struct _status_msg *p = _status_msg_list;
	 struct _status_msg *t = NULL;
	 while (p) {
		  if (p->id == id)
			   break;
		  t = p;
		  p = p->next;
	 }
	 if (p) {
		  if (t) {
			   t->next = p->next;
		  } else {
			   _status_msg_list = p->next;
		  }
		  if (_status_msg_old_list) { //add to the old list.
			   p->next = _status_msg_old_list;
			   _status_msg_old_list = p;
		  } else {
			   _status_msg_old_list = p;
			   p->next = NULL;
		  }
	 }

	 if (!_status_msg_list) {
		  elog_status_hide();
	 }
	 if (p) //we can't dereference, already freed
		  gtk_statusbar_pop(GTK_STATUSBAR(_status), id);
	 elog_status_normal(); //try to decolor

	 elog_glob_unlock();
}
//TODO: statusbar needs a button to see full length messages.
//TODO: statusbar coloring should work even with multiple messages.
//TODO: all messages should be insured their time of showing.
//TODO: repeate messages should be filtered out.


void elog_status_color_warn()
{
	 elog_glob_lock();
	 if (!_status_style) { 
		  _status_style = gtk_widget_get_modifier_style(_mainWindow);
		  g_object_ref(_status_style); //all of this is necessary!
		  gtk_widget_get_modifier_style(_mainWindow);
		  gtk_widget_modify_style(_mainWindow, _status_style);
	 }

	 GdkColor *color = malloc(sizeof *color);
	 color->red = 50000;
	 color->blue = 7000;
	 color->green = 45000;
	 gtk_widget_modify_bg(_mainWindow, GTK_STATE_NORMAL, color);
	 gtk_widget_modify_base(_mainWindow, GTK_STATE_NORMAL, color);
	 gtk_widget_modify_fg(_mainWindow, GTK_STATE_NORMAL, color);
	 free(color);
	 elog_glob_unlock();
}





int _status_wait(void *time) {
	 elog_glob_lock();
	 if (_status_msg_list->id != ((struct _status_msg *)time)->id) {
		  elog_glob_unlock();
		  return 1; //we're not being displayed right now
	 }
	 elog_glob_unlock();
	 usleep(50); 
	 elog_glob_lock();
	 ((struct _status_msg *)time)->time -= 50; 
	 if (((struct _status_msg *)time)->time <= 0) {
		  elog_status_pop_status(((struct _status_msg *)time)->id);
		  if (((struct _status_msg *)time)->color)
			   elog_status_normal();
		  free(time);
		  elog_glob_unlock();
		  return 0;
	 }
	 elog_glob_unlock();
	 return 1;
}

void elog_status_short_status(char *status, int disp_time_mu, int color)
{
	 elog_glob_lock();
	 int id = elog_status_push_status(status);
	 elog_status_color(id, color);
	 struct _status_msg *time = malloc(sizeof *time);
	 time->time = disp_time_mu;
	 time->id = id;
	 time->color = color;
	 if (color)
		  elog_status_color_warn();
	 elog_scnd_run_arg(_status_wait, "status_wait", time);
	 
	 elog_glob_unlock();
}




